package chen.yi.component.operate.log.aspect;

import chen.yi.component.operate.log.annotate.MyLog;
import chen.yi.component.operate.log.config.UsernameInvoker;
import chen.yi.component.operate.log.entity.OperateLog;
import chen.yi.component.operate.log.enums.ExecuteResultEnum;
import chen.yi.component.operate.log.mapper.OperateLogMapper;
import cn.hutool.json.JSONUtil;
import jakarta.servlet.http.HttpServletRequest;
import lombok.extern.slf4j.Slf4j;
import org.aspectj.lang.ProceedingJoinPoint;
import org.aspectj.lang.annotation.Around;
import org.aspectj.lang.annotation.Aspect;
import org.aspectj.lang.annotation.Pointcut;
import org.aspectj.lang.reflect.MethodSignature;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.core.annotation.AnnotationUtils;
import org.springframework.web.bind.annotation.PathVariable;
import org.springframework.web.context.request.RequestContextHolder;
import org.springframework.web.context.request.ServletRequestAttributes;
import org.springframework.web.servlet.HandlerMapping;

import java.lang.reflect.*;
import java.time.LocalDateTime;
import java.util.*;

/**
 * 业务日志记录
 *
 * @author huat
 **/
@Slf4j
@Aspect
public class MyLogAspect {
    @Autowired
    private OperateLogMapper operateLogMapper;



    /**
     * 注解切面
     */
    @Pointcut("@annotation(chen.yi.component.operate.log.annotate.MyLog)")
    public void aspect() {

    }

    /**
     * 记录保存日志
     *
     * @param joinPoint
     * @throws Throwable
     */
    @Around("aspect()")
    public Object saveLog(ProceedingJoinPoint joinPoint) throws Throwable {
        OperateLog operateLog = new OperateLog();
        //从切面织入点处通过反射机制获取织入点处的方法
        MethodSignature signature = (MethodSignature) joinPoint.getSignature();
        //获取类名
        String className = joinPoint.getTarget().getClass().getName();
        //获取切入点所在的方法
        Method method = signature.getMethod();
        //获取方法名
        String methodName = className + "." + method.getName() + "()";
        operateLog.setMethodName(methodName);
        //获取requestAttributes
        ServletRequestAttributes requestAttributes = (ServletRequestAttributes) RequestContextHolder.getRequestAttributes();
        // 获取请求上下文
        HttpServletRequest request = requestAttributes.getRequest();
        //获取请求方式
        String requestMethod = request.getMethod();
        operateLog.setRequestMethod(requestMethod);
        //获取log注解
        MyLog myLog = method.getAnnotation(MyLog.class);
        //获取操作类型
        String businessTypede = myLog.businessType().getCode();
        operateLog.setBusinessType(businessTypede);
        //模块ming
        String moduleName = myLog.moduleName();
        operateLog.setModuleName(moduleName);
        //判断传参方式是否为RESTful风格
        Parameter[] parameters = method.getParameters();
        //将参数转为json
        String paramsJson = "";
        boolean hasPathVariable = Arrays.stream(parameters).anyMatch(parameter -> AnnotationUtils.findAnnotation(parameter, PathVariable.class) != null);
        if (hasPathVariable) {
            Object attribute = request.getAttribute(HandlerMapping.URI_TEMPLATE_VARIABLES_ATTRIBUTE);
            paramsJson = JSONUtil.toJsonStr(attribute);
        } else {
            if(requestMethod.equalsIgnoreCase("GET")) {
                //获取请求参数
                Map<String, String[]> parameterMap = request.getParameterMap();
                paramsJson = JSONUtil.toJsonStr(parameterMap);
            }else{
                paramsJson=JSONUtil.toJsonStr(joinPoint.getArgs()[0]);

            }
        }
        operateLog.setRequestParams(paramsJson);
        operateLog.setOperateTime(LocalDateTime.now());
        operateLog.setOperationUsername((String)UsernameInvoker.getUsername(request));
//        operateLog.setOperationUsername(userInfo.getUsername(request));
        Object proceed =null;
        try {
            log.info("开始执行目标方法");
            proceed = joinPoint.proceed();
            log.info("目标方法执行结束");
            operateLog.setExecuteResult(ExecuteResultEnum.SUCCESS.getCode());
        } catch (Throwable e) {
            log.error("目标方法异常",e);
            operateLog.setExecuteResult(ExecuteResultEnum.ERROR.getCode());
            operateLog.setErrorMessage(e.getMessage());
            throw new Throwable(e);
        } finally {
            operateLogMapper.insert(operateLog);
            log.info("操作日志保存成功");

        }
        return proceed;
    }

}
